﻿// There are two different scripts: the first demonstrates the spike latency effect and the second shows the effect of spike latency on synaptic drive

// Note that DLL reference order is very important
#r @"C:\Users\Mira\Source\Repos\Spinula\Debug\SpikingNeuronLib.dll"
#r @"C:\Users\Mira\Source\Repos\Spinula\SpikingAnalyticsBaseLib\bin\Debug\SpikingAnalyticsBaseLib.dll"
#r @"C:\Users\Mira\Source\Repos\Spinula\SpikingAnalyticsLib\bin\Debug\SpikingAnalyticsLib.dll"
#I @"C:\Users\Mira\Source\Repos\Spinula\packages\RProvider.1.0.12"
#load "RProvider.fsx"
#I @"C:\Users\Mira\Source\Repos\Spinula\packages\Deedle.1.0.0"
#load "Deedle.fsx"
#r @"C:\Users\Mira\Source\Repos\Spinula\SpikingVisualisationRLib\bin\Debug\SpikingVisualisationRLib.dll"    // references Deedle
#r @"C:\Users\Mira\Source\Repos\Spinula\SpikingAnalyticsFrameLib\bin\Debug\SpikingAnalyticsFrameLib.dll"    // references Deedle

open System
open System.IO
open SpikingNeuronLib
open SpikingAnalyticsLib
open SpikingAnalyticsLib.GeneralUtilities
open SpikingAnalyticsLib.ResponseFingerPrint
open SpikingVisualisationRLib

//-----------------------------------------------------------------------------------------
// Test different combinations of input weights on spike latency

let ShowSpikeLatencyEffect numberOfInputs backgroundFiringRate =

    // generate trials that increment the total input to the output neuron
    let trials = GenerateWeightTrials numberOfInputs 30.0

    let outputNeuron = numberOfInputs

    let delays =
        let r = new Random()
        [ for i in 1..numberOfInputs -> r.Next(1, 21) ]     // between 1 and 20 inclusive

    let stimulus =
        let patternStimulationsPerSecond = 1
        let times = List.map (fun delay -> 20 - delay) delays
        let firingEvents =
            // zip times and neuron indexes to create firing events
            Seq.zip times [ for i in 0..numberOfInputs-1 -> i ]
            |> Seq.map (fun (time, nindex) ->
              new FiringEvent(time, nindex, EventLabel.Foreground)
            )
            |> Seq.toArray
        Stimulus.Create(patternStimulationsPerSecond, firingEvents)

    for sum, weights in trials do
        let specifier =
          CrossbarNetworkSpecifier.GetNetworkNto1Specifier(numberOfInputs, delays, weights)
        let network = CrossbarNetwork.CreateAdHocNetwork(specifier, None, false)
        let profile =
          new FrameProfile(network, Some(stimulus.Pattern), "Test", backgroundFiringRate, false)
        profile.PrintHistograms(outputNeuron, int sum)

// Test a 4 neuron network (3 inputs -> 1 output)
let numInputs = 3
let backgroundFiringRate = 0
ShowSpikeLatencyEffect numInputs backgroundFiringRate

//-----------------------------------------------------------------------------------------
// Show the effect of spike latency on synaptic drive

let ShowSpikeTimeMigration runSeconds delays (weights:float list) (outputFilePath:string) =

    let backgroundFiringRate = 0
    let patternStimulationsPerSecond = 5
    let plasticityEnabled = true

    let stimulus =
        let firingEvents =
            // zip times and neuron indexes and create firing events
            Seq.zip [ 0; 0; 0; ] [ 0; 1; 2; ]
            |> Seq.map (fun (time, nindex) ->
              new FiringEvent(time, nindex, EventLabel.Foreground)
            )
            |> Seq.toArray
        Stimulus.Create(patternStimulationsPerSecond, firingEvents)

    let specifier =
        let connections =
            // zip connected neurons, connection delays and weights
            Seq.zip3 [ (0, 3); (1, 3); (2, 3); ] delays weights
            |> Seq.map (fun ((preNeuron, postNeuron), delay, weight) ->
              new Connection(preNeuron, postNeuron, delay, weight)
            )
            |> Seq.toList
        new CrossbarNetworkSpecifier(4, 0, 30, connections)

    let network =
        let numberOfSamples = runSeconds * 1000
        let hiResCollector =
            let selectedNeurons = [ 0; 1; 2; 3; ]
            let selectedConnections = [ 0; 1; 2; ]
            let totalNeurons = specifier.TotalNeurons
            let totalConnections = specifier.Connections.Value.Count
            let numberOfMembraneSamples = numberOfSamples
            let numberOfWeightSamples = numberOfSamples
            let parameters =
              new OneMillisecTickDataCollectorParameters(selectedNeurons, totalNeurons,
                selectedConnections, totalConnections,
                numberOfMembraneSamples, numberOfWeightSamples)
            new OneMillisecTickDataCollector(parameters)
        CrossbarNetwork.CreateAdHocNetwork(specifier, Some(hiResCollector), false)

    // run the simulation
    network.TrainWithNoAction(stimulus, runSeconds, backgroundFiringRate)
    let dataCollector = network.OneMillisecondEventCollector.Value

    // plot the data
    MillisecondResolutionDataVisualisation.ShowCollectedWeightData(dataCollector)

    // save the data
    dataCollector.SaveConnectionData(outputFilePath)

let outputFolder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
let outputFilePath = Path.Combine(outputFolder, "connectionData.txt")

let runSeconds = 100
let delays = [ 10; 10; 15; ]      // for connections A, B and C
let weights = [ 8.0; 8.0; 8.0; ]  // for connections A, B and C
ShowSpikeTimeMigration runSeconds delays weights outputFilePath
